package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.bean.BeanUtils;
import com.zzyl.serve.domain.AlertData;
import com.zzyl.serve.domain.DeviceData;
import com.zzyl.serve.mapper.AlertDataMapper;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.service.IAlertDataService;
import com.zzyl.system.mapper.SysUserRoleMapper;
import net.sf.jsqlparser.statement.select.KSQLWindow;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.AlertRuleMapper;
import com.zzyl.serve.domain.AlertRule;
import com.zzyl.serve.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警规则Service业务层处理
 *
 * @author wangyc
 * @date 2025-08-26
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private SysUserRoleMapper roleMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IAlertDataService alertDataService;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return alertRuleMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return alertRuleMapper.deleteById(id);
    }

    /**
     * 报警规则过滤处理流程(定时任务，每分钟执行一次)
     */
    @Override
    public void alertFilter() {
        //1.判断已启用的报警规则为空
        //查询报警规则表中的数据已启用的数量是否为0，是的话直接结束
        long count = count(Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getStatus, 1));
        if (count == 0) {
            return;
        }
        //2.从redis获取设备上报数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.DEVICE_DATA_ALL_LAST);
        //3.设备上报的数据为空,直接结束
        if (ObjectUtil.isEmpty(values)) {
            return;
        }
        //4.将设备数据解析为DeviceData列表
        for (Object value : values) {
            String deviceStr = value.toString();
            if (StringUtils.isNotEmpty(deviceStr)) {
                List<DeviceData> deviceDataList = JSONUtil.toList(deviceStr, DeviceData.class);
                for (DeviceData deviceData : deviceDataList) {
                    //5.循环处理每个deviceData
                    getDeviceData(deviceData);
                }
            }
        }
    }
    /**
     * 对每一个设备的数据进行遍历，判断是否有异常
     *
     * @param deviceData
     */
    private void getDeviceData(DeviceData deviceData) {
        //1.设备数据上报时间已过去了1分钟
        //使用一个工具来计算当前时间与设备数据上报的时间差是否超过60秒，超过，就结束
        long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }
        //2.没有针对此设备的有效规则
        LambdaQueryWrapper<AlertRule> wrapper = Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .in(AlertRule::getIotId, deviceData.getIotId(), "-1"
                ).eq(AlertRule::getStatus, 1);

        List<AlertRule> rules = list(wrapper);
        if (ObjectUtil.isEmpty(rules)) {
            return;
        }

        //3.循环每个AlertRule
        for (AlertRule rule : rules) {
            getAlertRule(rule, deviceData);
        }
    }

    /**
     * 循环报警规则表 比对与设备数据是否有异常
     *
     * @param rule
     * @param deviceData
     */
    private void getAlertRule(AlertRule rule, DeviceData deviceData) {
        //4.不再规则的生效时间范围内 00:00:00~23:59:59
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        String alertEffectivePeriod = rule.getAlertEffectivePeriod();
        String[] period = alertEffectivePeriod.split("~");
        LocalTime start = LocalTime.parse(period[0]);
        LocalTime end = LocalTime.parse(period[1]);
        //要比较的时间对象.isBefore() 或.isAfter()
        if (alarmTime.isBefore(start) || alarmTime.isAfter(end)) {
            return;
        }
        String alertCountCacheKey = StringUtils.format(CacheConstants.ALERT_COUNT_KEY, deviceData.getIotId(), rule.getId());
        //5.设备数据没有异常(未达到规则的异常阈值),这里假设这个阈值就是数字类型
        double deviceValue = Double.parseDouble(deviceData.getDataValue());
        Long ruleValue = rule.getValue();
        if (">=".equals(rule.getOperator()) && deviceValue < ruleValue ||
                "<".equals(rule.getOperator()) && deviceValue >= ruleValue) {
            //异常持续周期
            //6.清理Redis中异常次数缓存
            redisTemplate.delete(alertCountCacheKey);
            return;
        }
        /*采用String存储设备数据异常的次数（周期）
         * key是：iot:alert:count:设备iotId:报警规则id
         * value是:已触发报警的次数
         * 有效期:页面上设置的沉默周期，过期后自动失效
         * 每触发报警一次，则value值+1
         * 如果某次设备上报数据正常，则置空异常次数
         * */
        //7.获取异常沉默标志
//        Integer alertSilentPeriod = rule.getAlertSilentPeriod();
        String alertSilentFlagKey = StringUtils.format(CacheConstants.ALERT_RULE_SILENT_KEY, deviceData.getIotId(), rule.getId());
        /*采用string存储异常沉默标记
         * key是：iot:alert:silent:设备iotId:报警规则id
         * value是: 随便，我们存储为1
         * 如果Redis中有此key,说明当前处于沉默周期内，无需报警
         * */
        //8.当前正在沉默周期内
        if (ObjectUtil.isNotEmpty(redisTemplate.opsForValue().get(alertSilentFlagKey))) {
            return;
        } else {
            //9.异常持续周期(异常次数)+1
            redisTemplate.opsForValue().increment(alertCountCacheKey);
        }
        //10.异常次数未到达持续周期要求
        Integer value1 = (Integer) redisTemplate.opsForValue().get(alertCountCacheKey);
        Integer duration = rule.getDuration();
        if (duration > value1 || value1 == null) {
            return;
        }
        //触发警报
        //清空异常持续周期缓存
        redisTemplate.delete(alertCountCacheKey);

        //设置沉默周期标识 这里标识默认设置为沉默周期
        redisTemplate.opsForValue().set(alertSilentFlagKey, rule.getAlertSilentPeriod(), rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //查找报警责任人
        String accessLocation = deviceData.getAccessLocation();
        Set<Long> userIds = new HashSet<>();
        if (rule.getAlertDataType() == 1) {
            //如果是设备异常，查找维修人员
            Set<Long> ids = roleMapper.selectUserIdByRoleName("维修工");
            userIds.addAll(ids);
        } else if (deviceData.getLocationType() == 1 && deviceData.getPhysicalLocationType() == 2) {
            //报警数据类型为老人异常
            //判断是否为床位类型
            Set<Long> ids = deviceMapper.selectNurseIdsByBedId(deviceData.getIotId());
            userIds.addAll(ids);
        } else if (deviceData.getLocationType() == 0) {
            //如果是随身设备
            Set<Long> ids = deviceMapper.selectNurseIdsByElderIotId(accessLocation);
            userIds.addAll(ids);
        }
        //添加超级管理员到责任人列表
        Set<Long> ids = roleMapper.selectUserIdByRoleName("超级管理员");
        userIds.addAll(ids);
        //批量保存报警数据
        List<AlertData> alertDataList = new ArrayList<>();
        //'报警原因，格式：功能名称+运算符+阈值+持续周期+聚合周期'
        String alertReason = rule.getFunctionName() + rule.getOperator() + rule.getValue()+",持续" + rule.getDuration()+"周期,发出警报";
        for (Long userId : userIds) {
            AlertData alertData = new AlertData();
            BeanUtils.copyProperties(deviceData, alertData);
            alertData.setId(null);
            alertData.setAlertRuleId(rule.getId());
            alertData.setAlertReason(alertReason);
            alertData.setType(rule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(userId);
            alertDataList.add(alertData);
        }
        System.out.println("alertDataList = " + alertDataList);
        alertDataService.saveBatch(alertDataList);

    }
}
